package com.genexususa.eo.connection.google.analytics;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.analytics.Analytics;
import com.google.api.services.analytics.AnalyticsScopes;
import com.google.api.services.analytics.model.GaData;
import com.google.api.services.analytics.model.McfData;

import java.io.*;
import java.util.Collections;
import java.util.List;

public class AnalyticsConnector {

    private static AnalyticsConnector instance = null;

    /**
     * Be sure to specify the name of your application. If the application name is {@code null} or
     * blank, the application will log a warning. Suggested format is "MyCompany-ProductName/1.0".
     */
    private static String APPLICATION_NAME = "L2TMEDIA";

    /**
     * Used to identify from which reporting profile to retrieve data.
     * Format is ga:xxx where xxx is your profile ID.
     */
    private static String TABLE_ID = "ga:69894122";

    /** Global instance of the HTTP transport. */
    private static  HttpTransport HTTP_TRANSPORT = new NetHttpTransport();

    /** Global instance of the JSON factory. */
    private static final JsonFactory JSON_FACTORY = new JacksonFactory();

    private static File DATA_STORE_DIR =
            new File(System.getProperty("user.home"), ".store/analytics_sample");


    private static String CLIENT_SECRETS =  "/client_secrets.json";

    public static Analytics analytics = null;

    private static FileDataStoreFactory DATA_STORE_FACTORY;

    public static void setCredentials(String dataStore, String clientSecrets){

        DATA_STORE_DIR = new File(System.getProperty("user.home"), dataStore);
        CLIENT_SECRETS = clientSecrets;


    }

    /** Authorizes the installed application to access user's protected data. */
    public static Credential authorize() throws Exception {

        // load client secrets
        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(
                JSON_FACTORY, new InputStreamReader(
                AnalyticsConnector.class.getResourceAsStream(CLIENT_SECRETS)));
        if (clientSecrets.getDetails().getClientId().startsWith("Enter")
                || clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) {
            System.out.println(
                    "Enter Client ID and Secret from https://code.google.com/apis/console/?api=analytics "
                            + "into analytics-cmdline-sample/src/main/resources/client_secrets.json");
            System.exit(1);
        }
        // set up authorization code flow
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
                HTTP_TRANSPORT, JSON_FACTORY, clientSecrets,
                Collections.singleton(AnalyticsScopes.ANALYTICS_READONLY)).setDataStoreFactory(
                DATA_STORE_FACTORY).build();
        // authorize
        return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
    }


    public Analytics initializeAnalytics() throws Exception  {
        HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
        DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
        // Authorization.
        Credential credential = authorize();

        // Set up and return Google Analytics API client.
        return new Analytics.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
                .setApplicationName(APPLICATION_NAME)
                .build();
    }

    public static AnalyticsConnector getInstance(){
        if(instance == null){
            return new AnalyticsConnector();
        }else{
            return instance;
        }
    }

    private void connect() throws Exception{

        if (analytics == null || (analytics.getApplicationName() != APPLICATION_NAME))
            analytics = initializeAnalytics();

    }


    public GaData executeQuery(String applicationName, String profileId, String startDate, String endDate,
                               List<String> metrics, List<String> dimensions, List<String> filters, List<String> segments, List<String> sortsCol,
                               int maxResults) throws Exception {

        AnalyticsConnector.APPLICATION_NAME = applicationName;
        this.connect();

        String tableId = "ga:" + profileId;

        return executeDataQuery(analytics, tableId, startDate, endDate,metrics, dimensions, filters, segments, sortsCol, maxResults);

    }

    public McfData executeConversionsQuery(String applicationName, String profileId, String startDate, String endDate,
                                           List<String> metrics, List<String> dimensions, List<String> filters, List<String> sortsCol,
                                           int maxResults) throws Exception {

        AnalyticsConnector.APPLICATION_NAME = applicationName;
        this.connect();

        String tableId = "ga:" + profileId;

        return executeConversionsDataQuery(analytics, tableId, startDate, endDate,metrics, dimensions, filters, sortsCol, maxResults);

    }

    private static McfData executeConversionsDataQuery(Analytics analytics, String tableId, String startDate, String endDate,
                                                       List<String> metricsCol, List<String> dimensionsCol,List<String> filtersCol, List<String> sortsCol,
                                                       int maxResults) throws IOException {
        /*
        return analytics.data().ga()
                .get(tableId,                  // Table Id.
                        "2013-02-01",              // Start date.
                        "2013-03-31",              // End date.
                        "ga:visits")               // Metrics.
                .setDimensions("ga:source,ga:keyword")
                .setSort("-ga:visits,ga:source")
                .setFilters("ga:medium==organic")
                .setMaxResults(25)
                .execute();
        */

        String metrics = toCommaSeparated(metricsCol);
        String dimensions = toCommaSeparated(dimensionsCol);
        String sorts = toCommaSeparated(sortsCol);
        String filters = toCommaSeparated(filtersCol);

        // to get MCF results you need to use (this should be used for Conversions)
        // analytics.data().mcf().get()

        Analytics.Data.Mcf.Get mcfGet = analytics.data().mcf().get(tableId, startDate, endDate, metrics);
        mcfGet = mcfGet.setSamplingLevel("HIGHER_PRECISION");

        if(dimensions.length() > 0)
            mcfGet = mcfGet.setDimensions(dimensions);
        if(sorts.length() > 0)
            mcfGet = mcfGet.setSort(sorts);
        if(filters.length() > 0)
            mcfGet = mcfGet.setFilters(filters);
        if (maxResults > 0)
            mcfGet = mcfGet.setMaxResults(maxResults);

        return mcfGet.execute();

    }


    /**
     * Returns the top 25 organic search keywords and traffic sources by visits. The
     * Core Reporting API is used to retrieve this data.
     *
     * @param analytics the Analytics service object used to access the API.
     * @param tableId the table ID from which to retrieve data.
     * @return the response from the API.
     * @throws java.io.IOException if an API error occured.
     */
    private static GaData executeDataQuery(Analytics analytics, String tableId, String startDate, String endDate,
                                           List<String> metricsCol, List<String> dimensionsCol,List<String> filtersCol, List<String> segmentsCol, List<String> sortsCol,
                                           int maxResults) throws IOException {
        /*
        return analytics.data().ga()
                .get(tableId,                  // Table Id.
                        "2013-02-01",              // Start date.
                        "2013-03-31",              // End date.
                        "ga:visits")               // Metrics.
                .setDimensions("ga:source,ga:keyword")
                .setSort("-ga:visits,ga:source")
                .setFilters("ga:medium==organic")
                .setMaxResults(25)
                .execute();
        */

        String metrics = toCommaSeparated(metricsCol);
        String dimensions = toCommaSeparated(dimensionsCol);
        String sorts = toCommaSeparated(sortsCol);
        String filters = toCommaSeparated(filtersCol);
        String segments = toCommaSeparated(segmentsCol);

        // to get MCF results you need to use (this should be used for Conversions)
        // analytics.data().mcf().get()

        Analytics.Data.Ga.Get gaGet = analytics.data().ga().get(tableId, startDate, endDate, metrics);
        gaGet = gaGet.setSamplingLevel("HIGHER_PRECISION");

        if(dimensions.length() > 0)
            gaGet = gaGet.setDimensions(dimensions);
        if(sorts.length() > 0)
            gaGet = gaGet.setSort(sorts);
        if(filters.length() > 0)
            gaGet = gaGet.setFilters(filters);
        if (maxResults > 0)
            gaGet = gaGet.setMaxResults(maxResults);
        if (segments.length() > 0)
            gaGet = gaGet.setSegment(segments);

        GaData result = null;

        try{
            result = gaGet.execute();
        } catch (NullPointerException nullEx){

            System.out.println(nullEx.getMessage());
            StringWriter writer = new StringWriter();
            nullEx.printStackTrace(new PrintWriter(writer));
            System.out.println(writer);

        }

        return result;


    }


    private static String toCommaSeparated(List<String> collection){
        String result = "";
        for (String item : collection){
            if((collection.size() - 1) > collection.indexOf(item)){
                result += item + ",";

            }else result += item;
        }
        return result;
    }
}
